/* The file is saved in UTF-8 codepage.
 * Check: «Stereotype», Section mark-§, Copyright-©, Alpha-α, Beta-β, Smile-☺
 */
package cz.pecinovsky.english.lootp.manager;



import cz.pecinovsky.english.lootp.manager.CanvasManager_Test.SprávcePlátna;
////////////////////////////////////////////////////////////////////////////////
//%%+  +++++ End of ignored starting test - place for imports +++++

//%A+ >=133x
import cz.pecinovsky.english.lootp.utility.NamedColor;
//%A-
//%A+ >135
import cz.pecinovsky.english.lootp.utility.Clock;
import cz.pecinovsky.english.lootp.utility.ITimed;
//%A-
//%A+ >=133x
import cz.pecinovsky.english.lootp.utility.IO;
import cz.pecinovsky.english.lootp.utility.Position;
import cz.pecinovsky.english.lootp.utility.Size;
//%A-

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;

import java.awt.event.KeyListener;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

import java.util.ArrayList;
//%A+ >135
import java.util.Collection;
//%A-
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
import java.util.Properties;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;



//%X+ xxxxx Start of the ovejumped text with declaration of the envelope xxxxx
/*******************************************************************************
 *<pre>
 * Předchozí:   Není - toto_je_nově_definovaná_třída
 *              Ttt v projektu Ppp
 * Následující: Prozatím není
 *
 * Projekt  Ppp
 *   + Přidáno
 *   - Odebráno
 *   ~ Změněno
 *</pre>
 *
 * @author Rudolf PECINOVSKÝ
 * @version 6.03.2017 — 2010-11-08
 */
public class CanvasManager_Test
{    private CanvasManager_Test() {}   static
////////////////////////////////////////////////////////////////////////////////
//%X- ----- End of the ovejumped text with declaration of the envelope -----


/*******************************************************************************
 * Třída <b><code>SprávcePlátna</code></b> slouží k jednoduchému kreslení
 * na virtuální plátno a připadné následné animaci nakreslených obrázků.
 * </p><p>
 * Třída neposkytuje veřejný konstruktor, protože chce, aby její instance
 * byla singleton, tj. aby se všechno kreslilo na jedno a to samé plátno.
 * Jediným způsobem, jak získat odkaz na instanci třídy
 * <code>SprávcePlátna</code>,
 * je volaní její statické metody <code>getInstance()</code>.
 * </p><p>
 * Třída <code>SprávcePlátna</code> funguje jako manažer, který dohlíží
 * na to, aby se po změně zobrazení některého z tvarů všechny ostatní tvary
 * řádně překreslily, aby byly správně zachyceny všechny překryvy
 * a aby se při pohybu jednotlivé obrazce vzájemně neodmazavaly.
 * Aby vše správně fungovalo, je možno použít jeden dvou přístupů:</p>
 * <ul>
 *   <li>Manažer bude obsah plátna překreslovat
 *       <b>v pravidelných intervalech</b>
 *       bez ohledu na to, jestli se na něm udála nějaká změna či ne.
 *       <ul><li>
 *       <b>Výhodou</b> tohoto přístupu je, že se zobrazované objekty
 *       nemusí starat o to, aby se manažer dozvěděl, že se jejich state změnil.
 *       </li><li>
 *       <b>Neýhodou</b> tohoto přístupu je naopak to, že manažer
 *       spotřebovává na neustálé překreslování jistou část výkonu
 *       procesoru, což může u pomalejších počítačů působit problémy.
 *       <br>&nbsp;</li></ul></li>
 *   <li>Manažer překresluje plátno <b>pouze na výslovné požádání</b>.
 *       <ul><li>
 *       <b>Výhodou</b> tohoto přístupu je úspora spotřebovaného výkonu
 *       počítače v období, kdy se na plátně nic neděje.
 *       </li><li>
 *       <b>Nevýhodou</b> tohoto přístupu je naopak to, že kreslené
 *       objekty musí na každou změnu svého stavu upozornit manažera,
 *       aby věděl, žed má plátno překreslit.
 *   </li>
 * </ul><p>
 * Třída <code>SprávcePlátna</code> požívá druhou z uvedených strategií,
 * tj. <b>překresluje plátno pouze na požádání</b>.
 * </p><p>
 * Obrazec, který chce být zobrazován na plátně, se musí nejprve přihlásit
 * u instance třídy <code>SprávcePlátna</code>, aby jej tato zařadila
 * mezi spravované obrazce (sada metod <code>add&hellip;</code>).
 * Přihlásit se však mohou pouze instance tříd, které implementují
 * rozhraní <code>IPaintable</code>.
 * </p><p>
 * Nepřihlášený obrazec nemá šanci býti zobrazen, protože na plátno
 * se může zobrazit pouze za pomoci kreslítka, jež může získat jedině od
 * instance třídy <code>SprávcePlátna</code>, ale ta je poskytuje pouze
 * instancím, které se přihlásily do její správy.
 * </p><p>
 * Obrazec, který již dále nemá byt kreslen, se muže odhlásit zavoláním
 * metody <code>remove(IPaintable)</code>.Zavoláním metody
 * <code>removeAll()</code> se ze seznamu spravovaných (a tím i z plátna)
 * odstraní všechny vykreslované obrazce.
 * </p><p>
 * Efektivitu vykreslování je možné ovlivnit voláním metody
 * <code>dontDraw()</code>, která pozastaví překreslování plátna po nahlášených
 * změnách. Její volání je výhodné např. v situaci, kdy je třeba vykreslit
 * obrazec složený z řady menších obrazců a bylo by nevhodné překreslovat
 * plátno po vykreslení každého z nich.
 * </p><p>
 * Do původního stavu převedeme plátno voláním metody <code>returnDraLevel()</code>,
 * která vrátí vykreslování do stavu před posledním voláním metody
 * <code>dontDraw()</code>. Nemůžeč se tedy stát, že by se při zavolání metody
 * <code>dontDraw()</code> v situaci, kdy je již vykreslování pozastaveno,
 * začalo po následém zavolání <code>returnDraLevel()</code> hned vykreslovat.
 * Po dvou voláních <code>returnDraLevel()</code> se začne vykreslovat až po
 * dvou zavoláních <code>returnDraLevel()</code>.
 * </p><p>
 * Proto plátno pouze žádáme, aby se vrátilo do toho kreslícího stavu,
 * ve kterém bylo v okamžiku, kdy jsme je naposledy žádali o to,
 * aby se přestalo překreslovat. Nemůže se tedy stát, že by se při zavolání
 * metody <code>dontDraw()</code> v situaci, kdy je již vykreslování
 * pozastaveno, začalo po následném zavolání <code>returnDraLevel()</code> hned
 * vykreslovat.
 * </p><p>
 * Každé zavolání metody <code>dontDraw()</code> musí být doplněno
 * odpovídajícím voláním <code>returnDraLevel()</code>. Teprve když poslední
 * <code>returnDraLevel()</code> odvolá první <code>dontDraw()</code>, bude
 * překreslování opět obnoveno.
 * </p>
 *
 * @author Rudolf PECINOVSKÝ
 * @version 6.03.2017 — 2010-11-08
 */
public final class SprávcePlátna implements ISprávce
{
//    static { System.out.println("Inicializace třídy SprávcePlátna"); }
//    { System.out.println("Inicializace instance třídy SprávcePlátna"); }
//== CONSTANT CLASS ATTRIBUTES =================================================

    /** Titulek okna aktivního plátna. */
    private static final String TITULEK_0  = "Canvas ovládané správcem";

    /** Počáteční políčková width current plochy plátna. */
    private static final int ŠÍŘKA_0 = 6;

    /** Počáteční políčková height current plochy plátna. */
    private static final int VÝŠKA_0 = 6;

    /** Počáteční color pozadí plátna. */
    private static final NamedColor POZADÍ_0 = NamedColor.CREAMY;

    /** Počáteční color čar mřížky. */
    private static final NamedColor BARVA_ČAR_0 = NamedColor.BLACK;

    /** Implicitní rozteč čtvercové sítě. */
    private static final int KROK_0 = 50;

    /** Maximální povolená velikost rozteče čtvercové sítě. */
    private static final int MAX_KROK = 200;

    /** Jediná instance třídy SprávcePlátna. */
    private static final SprávcePlátna CM;  // = new SprávcePlátna();

    static {
        String klíč = "rup.io.testmode";
        if (System.getProperty(klíč) != null &&
            Boolean.valueOf(System.getProperty(klíč)))
        {
            CM = new SprávcePlátna(new SprávceTestovací());
        }
        else {
            CM = new SprávcePlátna(new SprávceBěžný());
        }
    }



//== VARIABLE CLASS ATTRIBUTES =================================================
//== CONSTANT INSTANCE ATTRIBUTES ==============================================

    /** Implemetnace {code ISprávce}, na kterou se deleguje.*/
    private final ISprávce delegát;



//== VARIABLE INSTANCE ATTRIBUTES ==============================================
//== CLASS GETTERS AND SETTERS =================================================
//== OTHER NON-PRIVATE CLASS METHODS ===========================================

//##############################################################################
//== CONSTUCTORS AND FACTORY METHODS ===========================================

    /***************************************************************************
     * Jediná metoda umožnující získat odkaz na instanci plátna.
     * Vrací vsak pokaždé odkaz na stejnou instanci,
     * protože instance plátna je singleton.
     * <p>
     * Pokud instance při volaní metody ještě neexistuje,
     * metoda instanci vytvoří.</p>
     *
     * @return Instance třídy SprávcePlátna
     */
    public static SprávcePlátna getInstance()
    {
        return CM;
    }


    /***************************************************************************
     * Vytvoří instanci třídy - je volaná pouze jednou.
     *
     * @param delegát
     */
    @SuppressWarnings("serial")
    private SprávcePlátna(ISprávce delegát)
    {
        this.delegát = delegát;
    }



//== ABSTRACT METHODS ==========================================================
//== INSTANCE GETTERS AND SETTERS ==============================================

    /***************************************************************************
     * Nastaví rozměr plátna zadáním bodové velikosti políčka a
     * počtu políček ve vodorovném a svislém směru.
     * Při velikosti políčka = 1 se vypíná zobrazování mřížky.
     *
     * @param  krok    Nová bodová velikost políčka
     * @param  pŠířka  Nový počet políček vodorovně
     * @param  pVýška  Nový počet políček svisle
     */
    @Override
    public synchronized void setStepSize(int krok, int pŠířka, int pVýška)
    {
        delegát.setStepSize(krok, pŠířka, pVýška, null);
    }


    /***************************************************************************
     * Nastaví rozměr plátna zadáním bodové velikosti políčka a
     * počtu políček ve vodorovném a svislém směru.
     * Při velikosti políčka = 1 se vypíná zobrazování mřížky.
     *
     * @param  krok    Nová bodová velikost políčka
     * @param  pŠířka  Nový počet políček vodorovně
     * @param  pVýška  Nový počet políček svisle
     * @param  měnič   Objekt, který žádá o změnu rozměru. Jakmile je jednou
     *                 tento objekt nastaven, nesmí již rozměr plátna
     *                 měnit nikdo jiný.
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    synchronized void setStepSize(int krok, int pŠířka, int pVýška,
                                    Object měnič)
    {
        delegát.setStepSize(krok, pŠířka, pVýška, měnič);
    }


    /***************************************************************************
     * Vrátí vzdálenost čar mřížky = bodovou velikost políčka.
     *
     * @return Bodová velikost políčka
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
     public synchronized int getStep()
     {
         int krok = delegát.getStep();
         return krok;
     }


    /***************************************************************************
     * Nastaví vzdálenost čar mřížky = bodovou velikost políčka.
     * Při velikosti políčka = 1 se vypíná zobrazování mřížky.
     *
     * @param velikost  Nová bodová velikost políčka
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    synchronized void setStep(int velikost)
    {
        delegát.setStep(velikost);
    }


    /***************************************************************************
     * Vrátí počet sloupců plátna, tj. jeho políčkovou šířku.
     *
     * @return  Aktuální políčková width plátna (počet políček vodorovně)
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
    public synchronized int getFieldWidth()
    {
        int    columnSize = delegát.getFieldWidth();
        return columnSize;
    }


    /***************************************************************************
     * Vrátí bodovou šířku plátna.
     *
     * @return  Aktuální bodová width plátna (počet bodů vodorovně)
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    synchronized int getPixelWidth()
    {
        int    šířkaBodů = delegát.getPixelWidth();
        return šířkaBodů;
    }


    /***************************************************************************
     * Vrátí počet řádků plátna, tj. jeho políčkovou výšku.
     *
     * @return  Aktuální políčková height plátna (počet políček svisle)
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
    public synchronized int getFieldHeight()
    {
        int    rowSize = delegát.getFieldHeight();
        return rowSize;
    }


    /***************************************************************************
     * Vrátí bodovou výšku plátna.
     *
     * @return  Aktuální bodová height plátna (počet bodů svisle)
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    synchronized int getPixelHeight()
    {
        int    výškaBodů = delegát.getPixelHeight();
        return výškaBodů;
    }

//%A+ >135

    /***************************************************************************
     * Vrátí políčkový rozměr plátna, tj. šířku a výšku v polích.
     *
     * @return  Aktuální políčkový rozměr plátna
     */
//%A-
//%A+ <0    "To remove an editor warning
    @Override
//%A-
//%A+ >135
    public synchronized Size getSize()
    {
        return delegát.getSize();
    }

//%A-

    /***************************************************************************
     * Nastaví rozměr plátna zadáním jeho políčkové výsky a šířky.
     *
     * @param  columnSize  Nový počet políček vodorovně
     * @param  rowSize  Nový počet políček svisle
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    synchronized void setSize(int columnSize, int rowSize)
    {
        delegát.setSize(columnSize, rowSize);
    }

//%A+ >135

    /***************************************************************************
     * Nastaví rozměr plátna zadáním jeho políčkové výsky a šířky.
     *
     * @param  rozměr  Zadávaný rozměr v počtu políček
     */
//%A-
//%A+ <0    "To remove an editor warning
    @Override
//%A-
//%A+ >135
    public synchronized void setSize(Size rozměr)
    {
        delegát.setSize(rozměr.width, rozměr.height);
    }

//%A-

    /***************************************************************************
     * Vrátí informaci o tom, je-li zobrazována mřížka.
     *
     * @return Mřížka je zobrazována = true, není zobrazována = false.
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
    public synchronized boolean isGridVisible()
    {
        boolean mřížka = delegát.isGridVisible();
    	return  mřížka;
    }


    /***************************************************************************
     * V závislosti na hodntě parametru nastaví nebo potlačí
     * zobrazování čar mřížky.
     *
     * @param zobrazit  Jestli mřížku zobrazovat.
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
    public synchronized void setGridVisible(boolean zobrazit)
    {
        delegát.setGridVisible(zobrazit);
    }


    /***************************************************************************
     * Poskytuje informaci o aktuální viditelnosti okna.
     *
     * @return Je-li okno viditelné, vrací <b>true</b>, jinak vrací <b>false</b>
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    synchronized boolean isVisible()
    {
        boolean is = delegát.isVisible();
        return  is;
    }


    /***************************************************************************
     * V závislosti na hodntě parametru nastaví nebo potlačí viditelnost plátna.
     *
     * @param viditelné logická hodnota požadované viditelnost (true=viditelné)
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    synchronized void setVisible(boolean viditelné)
    {
        delegát.setVisible(viditelné);
    }


    /***************************************************************************
     * Vrátí aktuální barvu pozadí.
     *
     * @return  Nastavena color pozadí
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
    public synchronized NamedColor getBackgroundColor()
    {
        NamedColor   barvaPozadí = delegát.getBackgroundColor();
        return  barvaPozadí;
    }


    /***************************************************************************
     * Nastaví pro plátno barvu pozadí.
     *
     * @param  color  Nastavovaná color pozadí
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
    public synchronized void setBackgroundColor(NamedColor color)
    {
        delegát.setBackgroundColor(color);
    }


    /***************************************************************************
     * Pomocná metoda pro účely ladění aby bylo možno zkontrolovat,
     * ze na konci metody má semafor stejnou hodnotu, jako měl na počátku.
     *
     * @return  Stav vnitřního semaforu: >0  - nebude se kreslit,<br>
     *                                   ==0 - kreslí se,<br>
     *                                   <0  - chyba
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    synchronized int getDrawLevel()
    {
        int     nekreslit = delegát.getDrawLevel();
        return  nekreslit;
    }


    /***************************************************************************
     * Vrátí aktuální name v titulkové liště okna plátna.
     *
     * @return  Aktuální name okna
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
    public String getName()
    {
        String name = delegát.getName();
        return name;
    }


    /***************************************************************************
     * Nastaví name v titulkové liště okna plátna.
     *
     * @param name  Nastavovaný name
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
    public void setName(String name)
    {
        delegát.setName(name);
    }

//%A+ >135

    /***************************************************************************
     * Vrátí vodorovnou souřadnici aplikačního okna plátna.
     *
     * @return Position levého horního rohu aplikačního okna plátna.
     */
//%A-
//%A+ <0    "To remove an editor warning
    @Override
//%A-
//%A+ >135
    public Position getPosition()
    {
        Position  pozice = delegát.getPosition();
        return  pozice;
    }

//%A-

    /***************************************************************************
     * Nastaví pozici aplikačního okna aktivního plátna na obrazovce.
     *
     * @param x  Vodorovná souřadnice aplikačního okna plátna.
     * @param y  Svislá souřadnice aplikačního okna plátna.
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
    public void setPosition(int x, int y)
    {
        delegát.setPosition(x, y);
    }

//%A+ >135

    /***************************************************************************
     * Nastaví pozici aplikačního okna aktivního plátna na obrazovce.
     *
     * @param pozice  Požadovaná pozice aplikačního okna plátna na obrazovce.
     */
//%A-
//%A+ <0    "To remove an editor warning
    @Override
//%A-
//%A+ >135
    public void setPosition(Position pozice)
    {
        delegát.setPosition(pozice);
    }


    /***************************************************************************
     * Vrátí instanci třídy <code>Image</code> zobrazující zadaný výřez
     * AktivníhoPlátna.
     * @param x     Vodorovná pozice požadovaného výřezu
     * @param y     Svislá pozice požadovaného výřezu
     * @param width Šířka požadovaného výřezu v bodech
     * @param height Výška požadovaného výřezu v bodech
     * @return Instance třídy <code>Image</code> zobrazující zadaný výřez
     */
//%A-
//%A+ <0    "To remove an editor warning
    @Override
//%A-
//%A+ >135
    public Picture getPicture(int x, int y, int width, int height)
    {
        Picture obrázek = delegát.getPicture(x, y, width, height);
        return  obrázek;
    }

//%A-


//== OTHER NON-PRIVATE INSTANCE METHODS ========================================

    /***************************************************************************
     * Převede instanci na řetězec. Používá se především při ladění.
     *
     * @return Řetězcová reprezentace dané instance.
     */
    @Override
    public String toString()
    {
        return delegát.toString();
    }


    /***************************************************************************
     * Vykreslí všechny elementy.
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
    public synchronized void redraw()
    {
        delegát.redraw();
    }


    /***************************************************************************
     * Potlačí překreslování plátna, přesněji zvýší hladinu potlačení
     * překreslování o jedničku. Návratu do stavu před voláním této metody
     * se dosáhne zavoláním metody <code>returnDraLevel()</code>.</p>
     * <p>
     * Metody <code>dontDraw()</code> a <code>returnDraLevel()</code>
     * se tak chovají obdobně jako závorky, mezi nimiž je vykreslování
     * potlačeno.</p>
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
    public synchronized void dontDraw()
    {
        delegát.dontDraw();
    }


    /***************************************************************************
     * Vrátí překreslování do stavu před posledním voláním metody
     * <code>dontDraw()</code>. Předcházelo-li proto více volání metody
     * <code>dontDraw()</code>, začne se překreslovat až po odpovídajím počtu
     * zavolání metody <code>returnDraLevel()</code>.
     *
     * @throws IllegalStateException
     *         Je-li metoda volána aniž by předcházelo odpovídající volání
     *         <code>dontDraw()</code>.
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
    public synchronized void returnDraLevel()
    {
        delegát.returnDraLevel();
    }

//%A+ >135

    /***************************************************************************
     * Odpojí se od hodin a přestává synchronizovat
     * své překreslování s jejich tikotem.
     */
//%A-
//%A+ <0    "To remove an editor warning
    @Override
//%A-
//%A+ >135
    public void clockOff()
    {
        delegát.clockOff();
    }


    /***************************************************************************
     * Napojí se na hodiny, aby se na začátku tiku vypnulo kreslení
     * a na konci tiku opět zapnulo.
     */
//%A-
//%A+ <0    "To remove an editor warning
    @Override
//%A-
//%A+ >135
    public void clockOn()
    {
        delegát.clockOn();
    }

//%A-

    /***************************************************************************
     * Odstraní zadaný obrazec ze seznamu malovaných.
     * Byl-li obrazec v seznamu, překreslí plátno.
     *
     * @param obrazec  Odstraňovaný obrazec
     *
     * @return  true v případě, když obrazec v seznamu byl,
     *          false v případě, když nebylo co odstraňovat
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
    public synchronized boolean remove(IPaintable obrazec)
    {
        boolean ret = delegát.remove(obrazec);
        return  ret;
    }


    /***************************************************************************
     * Vyčisti plátno, tj. vyprázdní seznam malovaných
     * (odstraní z něj všechny obrazce).
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
    public synchronized void removeAll()
    {
        delegát.removeAll();
    }


    /***************************************************************************
     * Není-li zadaný obrazec v seznamu malovaných, přidá jej na konec
     * (bude se kreslit jako poslední, tj. na vrchu.
     * Byl-li obrazec opravdu přidán, překreslí plátno.
     * Objekty budou vždy kresleny v orderOf, v němž byly přidány do správy,
     * tj. v seznamu parametrů zleva doprava
     * a dříve zaregistrované objekty před objekty zaregistrovanými později.
     *
     * @param  obrazec  Přidávané obrazce
     * @return  Počet skutečně přidaných obrazců
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
    public synchronized int add(IPaintable... obrazec)
    {
        int     přidáno = delegát.add(obrazec);
        return  přidáno;
    }


    /***************************************************************************
     * Přidá obrazec do seznamu malovaných tak, aby byl kreslen
     * nad zadaným obrazcem.
     * Pokud již v seznamu byl, jenom jej přesune do zadané pozice.
     *
     * @param  současný  Obrazec, který má byt při kreslení pod
     *                    přidávaným obrazcem
     * @param  přidaný   Přidávaný obrazec
     *
     * @return  true  v případě, když byl obrazec opravdu přidán,
     *          false v případě, když již mezi zobrazovanými byl
     *                a pouze se přesunul do jiné urovné
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
    public synchronized boolean addOver(IPaintable současný, IPaintable přidaný)
    {
        boolean přidán = delegát.addOver(současný, přidaný);
        return  přidán;
    }


    /***************************************************************************
     * Přidá obrazec do seznamu malovaných tak, aby byl kreslen
     * pod zadaným obrazcem.
     * Pokud již v seznamu byl, jenom jej přesune do zadané pozice.
     *
     * @param  současný  Obrazec, který má byt při kreslení nad
     *                   přidávaným obrazcem
     * @param  přidaný   Přidávaný obrazec
     *
     * @return  true  v případě, když byl obrazec opravdu přidán,
     *          false v případě, když již mezi zobrazovanými byl
     *                a pouze se přesunul do jiné urovné
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
    public synchronized boolean addUnder(IPaintable současný, IPaintable přidaný)
    {
        boolean přidán = delegát.addUnder(současný, přidaný);
        return  přidán;
    }


    /***************************************************************************
     * Přidá obrazec do seznamu malovaných tak, aby byl kreslen
     * nad všemi obrazci.
     * Pokud již v seznamu byl, jenom jej přesune do požadované pozice.
     *
     * @param  přidaný   Přidávaný obrazec
     *
     * @return  true  v případě, když byl obrazec opravdu přidán,
     *          false v případě, když již mezi zobrazovanými byl
     *                a pouze se přesunul do jiné urovné
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    synchronized boolean addTop(IPaintable přidaný)
    {
        boolean přidán = delegát.addTop(přidaný);
        return  přidán;
    }


    /***************************************************************************
     * Přidá obrazec do seznamu malovaných tak, aby byl kreslen
     * pod zadaným obrazcem.
     * Pokud již v seznamu byl, jenom jej přesune do zadané pozice.
     *
     * @param  přidaný   Přidávaný obrazec
     *
     * @return  true  v případě, když byl obrazec opravdu přidán,
     *          false v případě, když již mezi zobrazovanými byl
     *                a pouze se přesunul do jiné urovné
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    synchronized boolean addBottom(IPaintable přidaný)
    {
        boolean přidán = delegát.addBottom(přidaný);
        return  přidán;
    }


    /***************************************************************************
     * Vrátí orderOf zadaného prvku v seznamu kreslených prvků.
     * Prvky se přitom kreslí v rostoucím orderOf, takže obrazec
     * s větším poradím je kreslen nad obrazcem s menším poradím.
     * Neni-li zadaný obrazec mezi kreslenými, vrátí -1.
     *
     * @param  obrazec  Objekt, na jehož kreslicí orderOf se dotazujeme
     *
     * @return  Pořadí obrazce; prvý kresleny obrazec má orderOf 0.
     *          Neni-li zadaný obrazec mezi kreslenými, vrátí -1.
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    synchronized int orderOf(IPaintable obrazec)
    {
        int     pořadí = delegát.orderOf(obrazec);
        return  pořadí;
    }


    /***************************************************************************
     * Vrátí nemodifikovatelný seznam všech spravovaných obrázků.
     *
     * @return  Požadovaný seznam
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    List<IPaintable> listOfPainted()
    {
        List<IPaintable> seznam = delegát.listOfPainted();
        return          seznam;
    }

//%A+ >135

    /***************************************************************************
     * Přihlásí posluchače změny velikosti pole.
     *
     * @param  posluchač  Přihlašovaný posluchač
     * @return  Informace o tom, byl-li posluchač doopravdy přidán -
     *          false oznamuje, že posluchač už byl přihlášen
     *          a nebylo jej proto třeba přidávat.
     */
//%A-
//%A+ <0    "To remove an editor warning
    @Override
//%A-
//%A+ >135
    public boolean addAdaptable(IAdaptable posluchač)
    {
        boolean přihlášen = delegát.addAdaptable(posluchač);
        return  přihlášen;
    }


    /***************************************************************************
     * Odhlásí posluchače změny velikosti pole.
     *
     * @param  posluchač  Odhlašovaný posluchač
     * @return  Informace o tom, byl-li posluchač doopravdy odebrán -
     *          false oznamuje, že posluchač už nebyl přihlášen
     *          a nebylo jej proto třeba odebírat.
     */
//%A-
//%A+ <0    "To remove an editor warning
    @Override
//%A-
//%A+ >135
    public boolean removeAdaptable(IAdaptable posluchač)
    {
        boolean odhlášen = delegát.removeAdaptable(posluchač);
        return  odhlášen;
    }


    /***************************************************************************
     * Nastaví, zda se mají přihlášeným posluchačům hlásit změny
     * velikosti kroku a vrátí původní nastavení.
     *
     * @param hlásit  Požadované nastavení (true=hlásit, false=nehlasit).
     * @return Původní nastavení
     */
//%A-
//%A+ <0    "To remove an editor warning
    @Override
//%A-
//%A+ >135
    public boolean referSizeChanges(boolean hlásit)
    {
        boolean ret = delegát.referSizeChanges(hlásit);
        return  ret;
    }

//%A-

    /***************************************************************************
     * Přihlásí posluchače událostí klávesnice.
     *
     * @param posluchač  Přihlašovaný posluchač
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    void addKeyboardListener(KeyListener posluchač)
    {
        delegát.addKeyboardListener(posluchač);
    }


    /***************************************************************************
     * Odhlásí posluchače klávesnice.
     *
     * @param posluchač  Odhlašovaný posluchač
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    void removeKeyboardListener(KeyListener posluchač)
    {
        delegát.removeKeyboardListener(posluchač);
    }


    /***************************************************************************
     * Přihlásí posluchače událostí myši.
     *
     * @param posluchač  Přihlašovaný posluchač
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    void addMouseListener(MouseListener posluchač)
    {
        delegát.addMouseListener(posluchač);
    }


    /***************************************************************************
     * Odhlásí posluchače myši.
     *
     * @param posluchač  Odhlašovaný posluchač
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    void removeMouseListener(MouseListener posluchač)
    {
        delegát.removeMouseListener(posluchač);
    }


    /***************************************************************************
     * Uloží obraz aktivního plátna do zadaného souboru.
     *
     * @param soubor Soubor, do nějž se má obraz plátna uložit
     */
//%A+ <0    "To remove an editor warning
    @Override
//%A-
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    void saveAsPicture(File soubor)
    {
        delegát.saveAsPicture(soubor);
    }


//== PRIVATE AND AUXILIARY CLASS METHODS =======================================
//== PRIVATE AND AUXILIARY INSTANCE METHODS ====================================
//== VNOŘENÉ A VNITŘNÍ TŘÍDY ===================================================

    static private final class SprávceBěžný implements ISprávce
    {
    //    static { System.out.println("Inicializace třídy SprávcePlátna"); }
    //    { System.out.println("Inicializace instance třídy SprávcePlátna"); }
    //== CONSTANT CLASS ATTRIBUTES =================================================

        /** Titulek okna aktivního plátna. */
        private static final String TITULEK_0  = "Canvas ovládané správcem";

        /** Počáteční políčková width current plochy plátna. */
        private static final int ŠÍŘKA_0 = 6;

        /** Počáteční políčková height current plochy plátna. */
        private static final int VÝŠKA_0 = 6;

        /** Počáteční color pozadí plátna. */
        private static final NamedColor POZADÍ_0 = NamedColor.CREAMY;

        /** Počáteční color čar mřížky. */
        private static final NamedColor BARVA_ČAR_0 = NamedColor.BLACK;

        /** Implicitní rozteč čtvercové sítě. */
        private static final int KROK_0 = 50;

        /** Maximální povolená velikost rozteče čtvercové sítě. */
        private static final int MAX_KROK = 200;

//        /** Jediná instance třídy SprávcePlátna. */
//        private static final SprávcePlátna CM = new SprávcePlátna();



    //== VARIABLE CLASS ATTRIBUTES =================================================
    //== CONSTANT INSTANCE ATTRIBUTES ==============================================

        /** Aplikační okno animačního plátna. */
        private final JFrame okno;

        /** Instance lokální třídy, která je zřízena proto, aby odstínila
         *  metody svého rodiče JPanel. */
        private final JPanel plátno;

        /** Seznam zobrazovaných předmětů. */
        private final List<IPaintable> předměty = new ArrayList<IPaintable>();
    //%A+ >135

        /** Seznam přihlášených přizpůsobivých předmětů. */
        private final Collection<IAdaptable> přizpůsobiví =
                                                new ArrayList<IAdaptable>();
    //%A-



    //== VARIABLE INSTANCE ATTRIBUTES ==============================================

        //Z venku neovlivnitelné Atributy pro zobrazeni plátna v aplikačním okně

            /** Vše se kreslí na obraz - ten se snadněji překreslí. */
            private Image obrazPlátna;

            /** Painter získané od obrazu plátna, na nějž se vlastně kreslí. */
            private Painter painter;

            /** Semafor bránící příliš častému překreslování. Překresluje se pouze
             *  je-li ==0. Nesmi byt <0. */
            private int nekreslit = 0;

            /** Příznak toho, že kreslení právě probíhá,
             *  takže vypínání nefunguje. */
            private boolean kreslím = false;

            /** Čáry zobrazující na plántě mřížku. */
            private Line[] vodorovná,   svislá;

        //Přímo ovlivnitelné atributy

            /** Rozteč čtvercové sítě. */
            private int krok = KROK_0;

            /** Zobrazuje-li se mřížka. */
            private boolean mřížka = true;

            /** NamedColor pozadí plátna. */
            private NamedColor barvaPozadí = POZADÍ_0;

            /** NamedColor čar mřížky. */
            private NamedColor barvaČar = BARVA_ČAR_0;

            /** Šířka current plochy plátna v udávaná v polích. */
            private int columnSize = ŠÍŘKA_0;

            /** Výška current plochy plátna v udávaná v polích. */
            private int rowSize = VÝŠKA_0;

            /** Šířka current plochy plátna v bodech. */
            private int šířkaBodů = ŠÍŘKA_0 * krok;

            /** Výška current plochy plátna v bodech. */
            private int výškaBodů = VÝŠKA_0 * krok;
    //%A+ >135

            /** Zda se mají přizpůsobiví upozorňovat na změny rozměru pole. */
            private boolean hlásitZměnyRozměru = true;
    //%A-

            /** Zda je možno měnit velikost kroku. */
            private Object vlastníkPovoleníZměnyKroku = null;

            /** Název v titulkové liště animačního plátna. */
            private String name  = TITULEK_0;

            /** Position plátna na obrazovace - při používání více obrazovek
             *  je občas třeba ji po zviditelnění obnovit. */
            Point pozice = new Point(0, 0);



    //== CLASS GETTERS AND SETTERS =================================================
    //== OTHER NON-PRIVATE CLASS METHODS ===========================================

    //##############################################################################
    //== CONSTUCTORS AND FACTORY METHODS ===========================================

        /***************************************************************************
         * Jediná metoda umožnující získat odkaz na instanci plátna.
         * Vrací vsak pokaždé odkaz na stejnou instanci,
         * protože instance plátna je singleton.
         * <p>
         * Pokud instance při volaní metody ještě neexistuje,
         * metoda instanci vytvoří.</p>
         *
         * @return Instance třídy SprávcePlátna
         */
        public static SprávcePlátna getInstance()
        {
            return CM;
        }


        /***************************************************************************
         * Vytvoří instanci třídy - je volaná pouze jednou.
         */
        @SuppressWarnings("serial")
        private SprávceBěžný()
        {
            okno  = new JFrame();          //Vytvoří nové aplikační okno
            okno.setTitle(name);
            konfiguraceZeSouboru();

            //Zavřením okna se zavře celá aplikace
            okno.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

            //Vlastní plátno je definováno jako instance anonymní třídy
            plátno =
                new JPanel()
                {   /** Povinně překrývaná abstraktní metoda třídy JPanel. */
                    @Override
                    public synchronized void paintComponent(Graphics g) {
                        g.drawImage(obrazPlátna, 0, 0, null);
                    }
                };//Konec definice třídy plátna
            okno.setContentPane(plátno);

        //Při kreslení čar se ptá APosuvný po správci plánta. Proto se mohou čáry
        //kreslit až poté, co bude jednináček (konstanta CM) inicializován.
            //Připraví a vykreslí prázdné plátno
            CM.setStepSize(KROK_0, ŠÍŘKA_0, VÝŠKA_0);
            //Nyní je znám rozměr plátna, tak můžeme umístit dialogová okna
            int x = okno.getX();
            int y = okno.getY() + okno.getHeight();
            IO.setDialogsPosition(x, y);
        }



    //== ABSTRACT METHODS ==========================================================
    //== INSTANCE GETTERS AND SETTERS ==============================================

        /***************************************************************************
         * Nastaví rozměr plátna zadáním bodové velikosti políčka a
         * počtu políček ve vodorovném a svislém směru.
         * Při velikosti políčka = 1 se vypíná zobrazování mřížky.
         *
         * @param  krok    Nová bodová velikost políčka
         * @param  pŠířka  Nový počet políček vodorovně
         * @param  pVýška  Nový počet políček svisle
         */
        @Override
        public synchronized void setStepSize(int krok, int pŠířka, int pVýška)
        {
            setStepSize(krok, pŠířka, pVýška, null);
        }


        /***************************************************************************
         * Nastaví rozměr plátna zadáním bodové velikosti políčka a
         * počtu políček ve vodorovném a svislém směru.
         * Při velikosti políčka = 1 se vypíná zobrazování mřížky.
         *
         * @param  krok    Nová bodová velikost políčka
         * @param  pŠířka  Nový počet políček vodorovně
         * @param  pVýška  Nový počet políček svisle
         * @param  měnič   Objekt, který žádá o změnu rozměru. Jakmile je jednou
         *                 tento objekt nastaven, nesmí již rozměr plátna
         *                 měnit nikdo jiný.
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        public
    //%A-
        @Override
        synchronized void setStepSize(int krok, int pŠířka, int pVýška,
                                        Object měnič)
        {
            //Kontrola, jetli rozměry mění ten, kdo je měnit smí
            if ((měnič != null)  &&
                (měnič != vlastníkPovoleníZměnyKroku))
            {
                if (vlastníkPovoleníZměnyKroku == null) {
                    vlastníkPovoleníZměnyKroku = měnič;
                } else {
                    throw new IllegalStateException(
                        "Změna kroku a rozměru není danému objektu povolena");
                }
            }
            //Ověření korektnosti zadaných parametrů
            Dimension obrazovka = Toolkit.getDefaultToolkit().getScreenSize();
            if ((krok   < 1)  ||
                (pŠířka < 2)  ||  (obrazovka.width  < šířkaBodů) ||
                (pVýška < 2)  ||  (obrazovka.height < výškaBodů))
            {
                throw new IllegalArgumentException(
                    "\nŠpatně zadané rozměry: " +
                    "\n  krok =" + krok  + " bodů," +
                    "\n  šířka=" + pŠířka + " polí = " + pŠířka*krok + " bodů," +
                    "\n  výška=" + pVýška + " polí = " + pVýška*krok + " bodů," +
                    "\n  obrazovka= " + obrazovka.width  + "×" +
                                        obrazovka.height + " bodů\n");
            }

            šířkaBodů = pŠířka * krok;
            výškaBodů = pVýška * krok;

            okno.setResizable(true);
            plátno.setPreferredSize(new Dimension(šířkaBodů, výškaBodů));
            okno.pack();
            okno.setResizable(false);

            obrazPlátna = plátno.createImage(šířkaBodů, výškaBodů);
            painter   = new Painter((Graphics2D)obrazPlátna.getGraphics());
            painter.setBackground(barvaPozadí);

            int starý    = this.krok;
            this.krok    = krok;
            this.columnSize = pŠířka;
            this.rowSize   = pVýška;

            připravČáry();

    //%A+ >135
            if (hlásitZměnyRozměru  &&  (krok != starý))
            {
                nekreslit++; {
                    for (IAdaptable ip : přizpůsobiví) {
                        ip.stepChanged(starý, krok);
                    }
                }nekreslit--;
            }
    //%A-
            IO.Correction.windowSize(okno);
            IO.Correction.windowLocation(okno);
            setVisible(true);
    //        redraw();  //Překresluje již předchozí metoda
        }


        /***************************************************************************
         * Vrátí vzdálenost čar mřížky = bodovou velikost políčka.
         *
         * @return Bodová velikost políčka
         */
         @Override public synchronized int getStep()
         {
             return krok;
         }


        /***************************************************************************
         * Nastaví vzdálenost čar mřížky = bodovou velikost políčka.
         * Při velikosti políčka = 1 se vypíná zobrazování mřížky.
         *
         * @param velikost  Nová bodová velikost políčka
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        synchronized void setStep(int velikost)
        {
            setStepSize(velikost, columnSize, rowSize);
        }


        /***************************************************************************
         * Vrátí počet sloupců plátna, tj. jeho políčkovou šířku.
         *
         * @return  Aktuální políčková width plátna (počet políček vodorovně)
         */
        @Override public synchronized int getFieldWidth()
        {
            return columnSize;
        }


        /***************************************************************************
         * Vrátí bodovou šířku plátna.
         *
         * @return  Aktuální bodová width plátna (počet bodů vodorovně)
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        synchronized int getPixelWidth()
        {
            return šířkaBodů;
        }


        /***************************************************************************
         * Vrátí počet řádků plátna, tj. jeho políčkovou výšku.
         *
         * @return  Aktuální políčková height plátna (počet políček svisle)
         */
        @Override public synchronized int getFieldHeight()
        {
            return rowSize;
        }


        /***************************************************************************
         * Vrátí bodovou výšku plátna.
         *
         * @return  Aktuální bodová height plátna (počet bodů svisle)
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        synchronized int getPixelHeight()
        {
            return výškaBodů;
        }

    //%A+ >135

        /***************************************************************************
         * Vrátí políčkový rozměr plátna, tj. šířku a výšku v polích.
         *
         * @return  Aktuální políčkový rozměr plátna
         */
        @Override public synchronized Size getSize()
        {
            return new Size(columnSize, rowSize);
        }

    //%A-

        /***************************************************************************
         * Nastaví rozměr plátna zadáním jeho políčkové výsky a šířky.
         *
         * @param  columnSize  Nový počet políček vodorovně
         * @param  rowSize  Nový počet políček svisle
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        synchronized void setSize(int columnSize, int rowSize)
        {
            setStepSize(krok, columnSize, rowSize);
        }

    //%A+ >135

        /***************************************************************************
         * Nastaví rozměr plátna zadáním jeho políčkové výsky a šířky.
         *
         * @param  rozměr  Zadávaný rozměr v počtu políček
         */
        @Override public synchronized void setSize(Size rozměr)
        {
            setSize(rozměr.width, rozměr.height);
        }

    //%A-

        /***************************************************************************
         * Vrátí informaci o tom, je-li zobrazována mřížka.
         *
         * @return Mřížka je zobrazována = true, není zobrazována = false.
         */
        @Override public synchronized boolean isGridVisible()
        {
            return mřížka;
        }


        /***************************************************************************
         * V závislosti na hodntě parametru nastaví nebo potlačí
         * zobrazování čar mřížky.
         *
         * @param zobrazit  Jestli mřížku zobrazovat.
         */
        @Override public synchronized void setGridVisible(boolean zobrazit)
        {
            mřížka = zobrazit;
            připravČáry();
            redraw();
        }


        /***************************************************************************
         * Poskytuje informaci o aktuální viditelnosti okna.
         *
         * @return Je-li okno viditelné, vrací <b>true</b>, jinak vrací <b>false</b>
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        synchronized boolean isVisible()
        {
            return okno.isVisible();
        }


        /***************************************************************************
         * V závislosti na hodntě parametru nastaví nebo potlačí viditelnost plátna.
         *
         * @param viditelné logická hodnota požadované viditelnost (true=viditelné)
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        synchronized void setVisible(boolean viditelné)
        {
            boolean změna = (isVisible() != viditelné);
            if (změna)
            {
                pozice = okno.getLocation();
                okno.setVisible(viditelné);
                if (viditelné)
                {
                    //Při více obrazovkách po zviáditelnění blbne =>
                    okno.setLocation(pozice);   //je třeba znovu nastavit pozici
                    okno.setAlwaysOnTop(true);
                    okno.toFront();
                    redraw();
                    okno.setAlwaysOnTop(false);
                }
            }
        }


        /***************************************************************************
         * Vrátí aktuální barvu pozadí.
         *
         * @return  Nastavena color pozadí
         */
        @Override public synchronized NamedColor getBackgroundColor()
        {
            return barvaPozadí;
        }


        /***************************************************************************
         * Nastaví pro plátno barvu pozadí.
         *
         * @param  color  Nastavovaná color pozadí
         */
        @Override public synchronized void setBackgroundColor(NamedColor color)
        {
            barvaPozadí = color;
            painter.setBackground(barvaPozadí);
            redraw();
        }


        /***************************************************************************
         * Pomocná metoda pro účely ladění aby bylo možno zkontrolovat,
         * ze na konci metody má semafor stejnou hodnotu, jako měl na počátku.
         *
         * @return  Stav vnitřního semaforu: >0  - nebude se kreslit,<br>
         *                                   ==0 - kreslí se,<br>
         *                                   <0  - chyba
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        synchronized int getDrawLevel()
        {
            return nekreslit;
        }


        /***************************************************************************
         * Vrátí aktuální name v titulkové liště okna plátna.
         *
         * @return  Aktuální name okna
         */
        @Override public String getName()
        {
            return okno.getTitle();
        }


        /***************************************************************************
         * Nastaví name v titulkové liště okna plátna.
         *
         * @param name  Nastavovaný name
         */
        @Override public void setName(String name)
        {
            okno.setTitle(this.name = name);
        }

    //%A+ >135

        /***************************************************************************
         * Vrátí vodorovnou souřadnici aplikačního okna plátna.
         *
         * @return Position levého horního rohu aplikačního okna plátna.
         */
        @Override public Position getPosition()
        {
            return new Position(okno.getX(), okno.getY());
        }

    //%A-

        /***************************************************************************
         * Nastaví pozici aplikačního okna aktivního plátna na obrazovce.
         *
         * @param x  Vodorovná souřadnice aplikačního okna plátna.
         * @param y  Svislá souřadnice aplikačního okna plátna.
         */
        @Override public void setPosition(int x, int y)
        {
            okno.setLocation(x, y);
            pozice = new Point(x, y);
        }

    //%A+ >135

        /***************************************************************************
         * Nastaví pozici aplikačního okna aktivního plátna na obrazovce.
         *
         * @param pozice  Požadovaná pozice aplikačního okna plátna na obrazovce.
         */
        @Override public void setPosition(Position pozice)
        {
            okno.setLocation(pozice.getX(), pozice.getY());
        }


        /***************************************************************************
         * Vrátí instanci třídy <code>Image</code> zobrazující zadaný výřez
         * AktivníhoPlátna.
         * @param x     Vodorovná pozice požadovaného výřezu
         * @param y     Svislá pozice požadovaného výřezu
         * @param width Šířka požadovaného výřezu v bodech
         * @param height Výška požadovaného výřezu v bodech
         * @return Instance třídy <code>Image</code> zobrazující zadaný výřez
         */
        @Override public Picture getPicture(int x, int y, int width, int height)
        {
            BufferedImage bim = getBufferedImage(x, y, width, height);
            return new Picture(0, 0, bim);
        }

    //%A-


    //== OTHER NON-PRIVATE INSTANCE METHODS ========================================

        /***************************************************************************
         * Převede instanci na řetězec. Používá se především při ladění.
         *
         * @return Řetězcová reprezentace dané instance.
         */
        @Override public String toString()
        {
            return getClass().getName() + "(krok=" + krok +
                    ", šířka=" + columnSize + ", výška=" + rowSize +
                    ", pozadí=" + barvaPozadí + ")";
        }


        /***************************************************************************
         * Vykreslí všechny elementy.
         */
        @Override public synchronized void redraw()
        {
            if (kreslím) {    //Právě překresluji - volám nepřímo sám sebe
                return;
            }
            if ((nekreslit == 0)  &&  isVisible())   //Mám kreslit a je proč
            {
                kreslím = true;
                synchronized(plátno)
                {
                    painter.fillRectangle(0, 0, šířkaBodů, výškaBodů,
                                        barvaPozadí);
                    if (mřížka  &&  (barvaČar != barvaPozadí))
                    {
                        //Budeme kreslit mřížku -- bude pod obrazci
                        for (int i=0;   i < columnSize;) {
                            svislá[i++].paint(painter);
                        }
                        for (int i=0;   i < rowSize;) {
                            vodorovná[i++].paint(painter);
                        }
                    }
                    for (IPaintable předmět : předměty) {
                        předmět.paint(painter);
                    }
                }//synchronized(obrazPlátna)
                plátno.repaint();       //Překreslí se aktualizované plátno
                kreslím = false;        //Už nekreslím
            }
        }


        /***************************************************************************
         * Potlačí překreslování plátna, přesněji zvýší hladinu potlačení
         * překreslování o jedničku. Návratu do stavu před voláním této metody
         * se dosáhne zavoláním metody <code>returnDraLevel()</code>.</p>
         * <p>
         * Metody <code>dontDraw()</code> a <code>returnDraLevel()</code>
         * se tak chovají obdobně jako závorky, mezi nimiž je vykreslování
         * potlačeno.</p>
         */
        @Override public synchronized void dontDraw()
        {
            nekreslit++;
        }


        /***************************************************************************
         * Vrátí překreslování do stavu před posledním voláním metody
         * <code>dontDraw()</code>. Předcházelo-li proto více volání metody
         * <code>dontDraw()</code>, začne se překreslovat až po odpovídajím počtu
         * zavolání metody <code>returnDraLevel()</code>.
         *
         * @throws IllegalStateException
         *         Je-li metoda volána aniž by předcházelo odpovídající volání
         *         <code>dontDraw()</code>.
         */
        @Override public synchronized void returnDraLevel()
        {
            if (nekreslit == 0) {
                throw new IllegalStateException(
                    "Vrácení do stavu kreslení musí přecházet zákaz!");
            }
            nekreslit--;
            if (nekreslit == 0)  {
                redraw();
            }
        }

    //%A+ >135

        /***************************************************************************
         * Odpojí se od hodin a přestává synchronizovat
         * své překreslování s jejich tikotem.
         */
        @Override public void clockOff()
        {
            Clock hodiny = Clock.getInstance();
            hodiny.setHeader (null);
            hodiny.setFooter(null);
        }


        /***************************************************************************
         * Napojí se na hodiny, aby se na začátku tiku vypnulo kreslení
         * a na konci tiku opět zapnulo.
         */
        @Override public void clockOn()
        {
            Clock hodiny = Clock.getInstance();
            hodiny.setHeader(new ITimed() {
                @Override
                public void tick() { dontDraw(); }
            });
            hodiny.setFooter(new ITimed() {
                @Override
                public void tick() { returnDraLevel(); }
            });
        }

    //%A-

        /***************************************************************************
         * Odstraní zadaný obrazec ze seznamu malovaných.
         * Byl-li obrazec v seznamu, překreslí plátno.
         *
         * @param obrazec  Odstraňovaný obrazec
         *
         * @return  true v případě, když obrazec v seznamu byl,
         *          false v případě, když nebylo co odstraňovat
         */
        @Override public synchronized boolean remove(IPaintable obrazec)
        {
            boolean ret = předměty.remove(obrazec);
            if (ret) {
                redraw();
            }
            return ret;
        }


        /***************************************************************************
         * Vyčisti plátno, tj. vyprázdní seznam malovaných
         * (odstraní z něj všechny obrazce).
         */
        @Override public synchronized void removeAll()
        {
            dontDraw(); {
                ListIterator<IPaintable> it = předměty.listIterator();
                while (it.hasNext()) {
                    it.next();
                    it.remove();
                }
            } returnDraLevel();
        }


        /***************************************************************************
         * Není-li zadaný obrazec v seznamu malovaných, přidá jej na konec
         * (bude se kreslit jako poslední, tj. na vrchu.
         * Byl-li obrazec opravdu přidán, překreslí plátno.
         * Objekty budou vždy kresleny v orderOf, v němž byly přidány do správy,
         * tj. v seznamu parametrů zleva doprava
         * a dříve zaregistrované objekty před objekty zaregistrovanými později.
         *
         * @param  obrazec  Přidávané obrazce
         * @return  Počet skutečně přidaných obrazců
         */
        @Override public synchronized int add(IPaintable... obrazec)
        {
            int počet = 0;
            dontDraw(); {
                for (IPaintable ik : obrazec)
                {
                    if (! předměty.contains(ik)) {
                        předměty.add(ik);
                        počet++;
                    }
                }
            } returnDraLevel();
            return počet;
        }


        /***************************************************************************
         * Přidá obrazec do seznamu malovaných tak, aby byl kreslen
         * nad zadaným obrazcem.
         * Pokud již v seznamu byl, jenom jej přesune do zadané pozice.
         *
         * @param  současný  Obrazec, který má byt při kreslení pod
         *                    přidávaným obrazcem
         * @param  přidaný   Přidávaný obrazec
         *
         * @return  true  v případě, když byl obrazec opravdu přidán,
         *          false v případě, když již mezi zobrazovanými byl
         *                a pouze se přesunul do jiné urovné
         */
        @Override public synchronized boolean addOver(IPaintable současný, IPaintable přidaný)
        {
            boolean nebyl = ! předměty.remove(přidaný);
            int kam = předměty.indexOf(současný);
            if (kam < 0)
            {
                throw new IllegalArgumentException(
                    "Referenční objekt není na plátně zobrazován!");
            }
            předměty.add(kam+1, přidaný);
            redraw();
            return nebyl;
        }


        /***************************************************************************
         * Přidá obrazec do seznamu malovaných tak, aby byl kreslen
         * pod zadaným obrazcem.
         * Pokud již v seznamu byl, jenom jej přesune do zadané pozice.
         *
         * @param  současný  Obrazec, který má byt při kreslení nad
         *                   přidávaným obrazcem
         * @param  přidaný   Přidávaný obrazec
         *
         * @return  true  v případě, když byl obrazec opravdu přidán,
         *          false v případě, když již mezi zobrazovanými byl
         *                a pouze se přesunul do jiné urovné
         */
        @Override public synchronized boolean addUnder(IPaintable současný, IPaintable přidaný)
        {
            boolean nebyl = ! předměty.remove(přidaný);
            int kam = předměty.indexOf(současný);
            if (kam < 0)
            {
                throw new IllegalArgumentException(
                    "Referenční objekt není na plátně zobrazován!");
            }
            předměty.add(kam, přidaný);
            redraw();
            return nebyl;
        }


        /***************************************************************************
         * Přidá obrazec do seznamu malovaných tak, aby byl kreslen
         * nad všemi obrazci.
         * Pokud již v seznamu byl, jenom jej přesune do požadované pozice.
         *
         * @param  přidaný   Přidávaný obrazec
         *
         * @return  true  v případě, když byl obrazec opravdu přidán,
         *          false v případě, když již mezi zobrazovanými byl
         *                a pouze se přesunul do jiné urovné
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        synchronized boolean addTop(IPaintable přidaný)
        {
            boolean nebyl = ! předměty.remove(přidaný);
            předměty.add(přidaný);
            redraw();
            return nebyl;
        }


        /***************************************************************************
         * Přidá obrazec do seznamu malovaných tak, aby byl kreslen
         * pod zadaným obrazcem.
         * Pokud již v seznamu byl, jenom jej přesune do zadané pozice.
         *
         * @param  přidaný   Přidávaný obrazec
         *
         * @return  true  v případě, když byl obrazec opravdu přidán,
         *          false v případě, když již mezi zobrazovanými byl
         *                a pouze se přesunul do jiné urovné
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        synchronized boolean addBottom(IPaintable přidaný)
        {
            boolean nebyl = ! předměty.remove(přidaný);
            předměty.add(0, přidaný);
            redraw();
            return nebyl;
        }


        /***************************************************************************
         * Vrátí orderOf zadaného prvku v seznamu kreslených prvků.
         * Prvky se přitom kreslí v rostoucím orderOf, takže obrazec
         * s větším poradím je kreslen nad obrazcem s menším poradím.
         * Neni-li zadaný obrazec mezi kreslenými, vrátí -1.
         *
         * @param  obrazec  Objekt, na jehož kreslicí orderOf se dotazujeme
         *
         * @return  Pořadí obrazce; prvý kresleny obrazec má orderOf 0.
         *          Neni-li zadaný obrazec mezi kreslenými, vrátí -1.
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        synchronized int orderOf(IPaintable obrazec)
        {
            return předměty.indexOf(obrazec);
        }


        /***************************************************************************
         * Vrátí nemodifikovatelný seznam všech spravovaných obrázků.
         *
         * @return  Požadovaný seznam
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        List<IPaintable> listOfPainted()
        {
            return Collections.unmodifiableList(předměty);
        }

    //%A+ >135

        /***************************************************************************
         * Přihlásí posluchače změny velikosti pole.
         *
         * @param  posluchač  Přihlašovaný posluchač
         * @return  Informace o tom, byl-li polsuchač doopravdy přidán -
         *          false oznamuje, že posluchač už byl přihlášen
         *          a nebylo jej proto třeba přidávat.
         */
        @Override public boolean addAdaptable(IAdaptable posluchač)
        {
            return přizpůsobiví.add(posluchač);
        }


        /***************************************************************************
         * Odhlásí posluchače změny velikosti pole.
         *
         * @param  posluchač  Odhlašovaný posluchač
         * @return  Informace o tom, byl-li polsuchač doopravdy odebrán -
         *          false oznamuje, že posluchač už nebyl přihlášen
         *          a nebylo jej proto třeba odebírat.
         */
        @Override public boolean removeAdaptable(IAdaptable posluchač)
        {
            return přizpůsobiví.remove(posluchač);
        }


        /***************************************************************************
         * Nastaví, zda se mají přihlášeným posluchačům hlásit změny
         * velikosti kroku a vrátí původní nastavení.
         *
         * @param hlásit  Požadované nastavení (true=hlásit, false=nehlasit).
         * @return Původní nastavení
         */
        @Override public boolean referSizeChanges(boolean hlásit)
        {
            boolean ret = hlásitZměnyRozměru;
            hlásitZměnyRozměru = hlásit;
            return ret;
        }

    //%A-

        /***************************************************************************
         * Přihlásí posluchače událostí klávesnice.
         *
         * @param posluchač  Přihlašovaný posluchač
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        void addKeyboardListener(KeyListener posluchač)
        {
            okno.addKeyListener(posluchač);
        }


        /***************************************************************************
         * Odhlásí posluchače klávesnice.
         *
         * @param posluchač  Odhlašovaný posluchač
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        void removeKeyboardListener(KeyListener posluchač)
        {
            okno.removeKeyListener(posluchač);
        }


        /***************************************************************************
         * Přihlásí posluchače událostí myši.
         *
         * @param posluchač  Přihlašovaný posluchač
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        void addMouseListener(MouseListener posluchač)
        {
            okno.addMouseListener(posluchač);
        }


        /***************************************************************************
         * Odhlásí posluchače myši.
         *
         * @param posluchač  Odhlašovaný posluchač
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        void removeMouseListener(MouseListener posluchač)
        {
            okno.removeMouseListener(posluchač);
        }


        /***************************************************************************
         * Uloží obraz aktivního plátna do zadaného souboru.
         *
         * @param soubor Soubor, do nějž se má obraz plátna uložit
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        void saveAsPicture(File soubor)
        {
            BufferedImage bim = getBufferedImage();
            try {
                ImageIO.write(bim, "PNG", soubor);
            } catch(IOException exc)  {
                throw new RuntimeException(
                    "\nObraz aktivního plátna se nepodařilo uložit do souboru " +
                    soubor,  exc);
            }
        }


    //== PRIVATE AND AUXILIARY CLASS METHODS =======================================
    //== PRIVATE AND AUXILIARY INSTANCE METHODS ====================================

        /***************************************************************************
         * Vrátí obrázek na aktivním plátně.
         * @return Obsah plátna jako obrázek
         */
        private BufferedImage getBufferedImage()
        {
            if (obrazPlátna instanceof BufferedImage) {
                return (BufferedImage) obrazPlátna;         //==========>
            }
            else {
                return getBufferedImage(0, 0, šířkaBodů, výškaBodů);
            }
        }


        /***************************************************************************
         * Vrátí obrázek výřezu na aktivním plátně.
         *
         * @param x
         * @param y
         * @param šířka
         * @param výška
         * @return Výřez obsahu plátna jako obrázek
         */
        private BufferedImage getBufferedImage(int x, int y,
                                               int šířka, int výška)
        {
            BufferedImage ret = new BufferedImage(šířka, výška,
                                                   BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = (Graphics2D)ret.getGraphics();
            g2d.drawImage(obrazPlátna, -x, -y, Painter.IMG_OBSERVER);
            return ret;
        }


        /***************************************************************************
         * Inicializuje některé parametry z konfiguračního souboru.
         * Tento soubor je umístěn v domovském adresáři uživatele
         * ve složce {@code .rup} v souboru {@code bluej.properties}.
         */
        private void konfiguraceZeSouboru() {
            Properties sysProp = System.getProperties();
            String     userDir = sysProp.getProperty("user.home");
            File       rupFile = new File(userDir, ".rup/bluej.properties");
            Properties rupProp = new Properties();
            try {
                Reader reader = new FileReader(rupFile);
                rupProp.load(reader);
                reader.close();
                String sx = rupProp.getProperty("canvas.x");
                String sy = rupProp.getProperty("canvas.y");
                int x = Integer.parseInt(rupProp.getProperty("canvas.x"));
                int y = Integer.parseInt(rupProp.getProperty("canvas.y"));
                pozice = new Point(x, y);
            }catch(Exception e)  {
                pozice = new Point(0, 0);
            }
            okno.setLocation(pozice);
        }


        /***************************************************************************
         * Připraví čáry vyznačující jednotlivá pole aktivního plátna.
         * Pokud se čáry kreslit nemají, vyprázdní odkazy na ně.
         */
        private void připravČáry()
        {
            if (mřížka  &&  (krok > 1))
            {
                if ((svislá == null)  ||  (svislá.length != columnSize)) {
                    svislá = new Line[columnSize];
                }
                if ((vodorovná == null)  ||  (vodorovná.length != rowSize)) {
                    vodorovná = new Line[rowSize];
                }
                for (int i=0, x=krok;   i < columnSize;      i++, x+=krok) {
                    svislá[i] = new Line(x, 0, x, výškaBodů, barvaČar);
                }
                for (int i=0, y=krok;   i < rowSize;   i++, y+=krok) {
                    vodorovná[i] = new Line(0, y, šířkaBodů, y, barvaČar);
                }
            }
            else
            {
                //Uvolnění doposud používaných instancí
                svislá    = null;
                vodorovná = null;
                mřížka    = false;
            }
        }



    //== VNOŘENÉ A VNITŘNÍ TŘÍDY ===================================================
    //== TESTING CLASSES AND METHODS ===============================================
    //%A+ >135

        private static void w(){try{
            Thread.sleep(10);}catch(InterruptedException e){}
        }

        /***************************************************************************
         * Testovaci metoda
         */
        public static void test() {
            CM.add(new Rectangle());                    w();
            CM.add(new Ellipse(), new Triangle());   w();
            IO.inform("Výchozí obrázek - budu vyjímat výřez");
            Picture obr = CM.getPicture(50, 0, 75, 75);   w();
            CM.add(obr);                               w();
            CM.setBackgroundColor(NamedColor.BLACK);               w();
            IO.inform("Obrázek přidaný?");
            obr.setPosition(100, 50);                       w();
            IO.inform("Posunutý?");
            obr.setSize(150, 150);                        w();
            IO.inform("Zvětšený?");
            CM.setStepSize(50, 5, 2);
    //        CM.setStepSize(1, 50, 50);
    //        CM.saveAsPicture(new File("D:/SMAZAT.PNG"));

            System.exit(0);
        }


        /***************************************************************************
         * Testovaci metoda
         */
        public static void test2()
        {
            IO.inform("Hotovo");
            CM.add(new Rectangle   (0, 0, 300, 300));
            CM.add(new Ellipse     (0, 0, 300, 300));
            CM.add(new Triangle(0, 0, 300, 300));
            IO.inform("Nakresleno");

            System.exit(0);
        }
        /** @param args Paremtry příkazového řádku - nepoužité  */
        public static void main(String[] args) { test2(); } /**/
    //%A-
    }


///#############################################################################
///#############################################################################
///#############################################################################

    /***************************************************************************
     * Rozhraní {@code IPlátno} deklaruje kompletní sadu metod
     * implementovaných třídou {@code Canvas}.
     * Představuje společného rodiče výkonné a testovací třídy,
     * tj. třídy {@code PlátnoBěžné} a třídy {@code PlátnoTestovací}.
     */
    private static class SprávceTestovací implements ISprávce
    {
    //== CONSTANT CLASS ATTRIBUTES =================================================
    //== VARIABLE CLASS ATTRIBUTES =================================================
    //== STATIC CONSTRUCTOR (CLASS INITIALIZER, STATIC INICITALIZING BLOCK) ======== 
    //== CONSTANT INSTANCE ATTRIBUTES ==============================================
    //== VARIABLE INSTANCE ATTRIBUTES ==============================================
    //== CLASS GETTERS AND SETTERS =================================================
    //== OTHER NON-PRIVATE CLASS METHODS ===========================================

    //##############################################################################
    //== CONSTUCTORS AND FACTORY METHODS ===========================================

        SprávceTestovací()
        {
        }

    //== ABSTRACT METHODS ==========================================================
    //== INSTANCE GETTERS AND SETTERS ==============================================

    @Override public void setStepSize(int krok, int pŠířka, int pVýška){
            přidejVoláníMetody("setKrokRozměr");
        }
    @Override public void setPosition(int x, int y) {
            přidejVoláníMetody("setPozice");
        }
    @Override public int getStep() {
        přidejVoláníMetody("setPozice");
        return 0;
    }
    @Override public int getFieldWidth() {
        přidejVoláníMetody("setPozice");
        return 0;
    }
    @Override public int getFieldHeight() {
        přidejVoláníMetody("setPozice");
        return 0;
    }
    @Override public boolean isGridVisible() {
        přidejVoláníMetody("setPozice");
        return false;
    }
    @Override public void setGridVisible(boolean zobrazit) {
        přidejVoláníMetody("setPozice");
    }
    @Override public NamedColor getBackgroundColor() {
        přidejVoláníMetody("setPozice");
        return null;
    }
    @Override public void setBackgroundColor(NamedColor color) {
        přidejVoláníMetody("setPozice");
    }
    @Override public String getName() {
        přidejVoláníMetody("setPozice");
        return null;
    }
    @Override public void setName(String name) {
        přidejVoláníMetody("setPozice");
    }
//%A+ >135
    @Override public void setStepSize(int krok, int pŠířka, int pVýška, Object měnič) {
        přidejVoláníMetody("setPozice");
    }
    @Override public void setStep(int velikost) {
        přidejVoláníMetody("setPozice");
    }
    @Override public int getPixelWidth() {
        přidejVoláníMetody("setPozice");
        return 0;
    }
    @Override public int getPixelHeight() {
        přidejVoláníMetody("setPozice");
        return 0;
    }
    @Override public Size getSize() {
        přidejVoláníMetody("setPozice");
        return null;
    }
    @Override public void setSize(int columnSize, int rowSize) {
        přidejVoláníMetody("setPozice");
    }
    @Override public void setSize(Size rozměr) {
        přidejVoláníMetody("setPozice");
    }
    @Override public boolean isVisible() {
        přidejVoláníMetody("setPozice");
        return false;
    }
    @Override public void setVisible(boolean viditelné) {
        přidejVoláníMetody("setPozice");
    }
    @Override public int getDrawLevel() {
        přidejVoláníMetody("setPozice");
        return 0;
    }
    @Override public Position getPosition() {
        přidejVoláníMetody("setPozice");
        return null;
    }
    @Override public void setPosition(Position pozice) {
        přidejVoláníMetody("setPozice");
    }
    @Override public Picture getPicture(int x, int y, int šířka, int výška) {
        přidejVoláníMetody("setPozice");
        return null;
    }
//%A-



    //== OTHER NON-PRIVATE INSTANCE METHODS ========================================

    @Override public void redraw() {
        přidejVoláníMetody("setPozice");
    }
    @Override public void dontDraw() {
        přidejVoláníMetody("setPozice");
    }
    @Override public void returnDraLevel() {
        přidejVoláníMetody("setPozice");
    }
    @Override public boolean remove(IPaintable obrazec) {
        přidejVoláníMetody("setPozice");
        return false;
    }
    @Override public void removeAll() {
        přidejVoláníMetody("setPozice");
    }
    @Override public int add(IPaintable... obrazec) {
        přidejVoláníMetody("setPozice");
        return 0;
    }
    @Override public boolean addOver(IPaintable současný, IPaintable přidaný) {
        přidejVoláníMetody("setPozice");
        return false;
    }
    @Override public boolean addUnder(IPaintable současný, IPaintable přidaný) {
        přidejVoláníMetody("setPozice");
        return false;
    }
//%A+ >135
    @Override public void clockOff() {
        přidejVoláníMetody("setPozice");
    }
    @Override public void clockOn() {
        přidejVoláníMetody("setPozice");
    }
    @Override public boolean addTop(IPaintable přidaný) {
        přidejVoláníMetody("setPozice");
        return false;
    }
    @Override public boolean addBottom(IPaintable přidaný) {
        přidejVoláníMetody("setPozice");
        return false;
    }
    @Override public int orderOf(IPaintable obrazec) {
        přidejVoláníMetody("setPozice");
        return 0;
    }
    @Override public List<IPaintable> listOfPainted() {
        přidejVoláníMetody("setPozice");
        return null;
    }
    @Override public boolean addAdaptable(IAdaptable posluchač) {
        přidejVoláníMetody("setPozice");
        return false;
    }
    @Override public boolean removeAdaptable(IAdaptable posluchač) {
        přidejVoláníMetody("setPozice");
        return false;
    }
    @Override public boolean referSizeChanges(boolean hlásit) {
        přidejVoláníMetody("setPozice");
        return false;
    }
    @Override public void addKeyboardListener(KeyListener posluchač) {
        přidejVoláníMetody("setPozice");
    }
    @Override public void removeKeyboardListener(KeyListener posluchač) {
        přidejVoláníMetody("setPozice");
    }
    @Override public void addMouseListener(MouseListener posluchač) {
        přidejVoláníMetody("setPozice");
    }
    @Override public void removeMouseListener(MouseListener posluchač) {
        přidejVoláníMetody("setPozice");
    }
    @Override public void saveAsPicture(File soubor) {
        přidejVoláníMetody("setPozice");
    }
//%A-



    //== PRIVATE AND AUXILIARY CLASS METHODS =======================================

        /***************************************************************************
         * Zaznamená volání metody do systémových proměnných. Systémové proměnné
         * začánají na {@code rup.io.method.} a následuje označení metody
         * ve tvaru {@code jménoMetody_typParametru1_typParametru2}
         *
         * @param metoda   Označení metody
         */
        private static void přidejVoláníMetody(String metoda)
        {
            String kód = "rup.io.method." + metoda;
            String hodnota = System.getProperty(kód, "0");
            int počet = Integer.valueOf(hodnota);
            počet++;
            System.setProperty(kód, "" + počet);
        }

    //== PRIVATE AND AUXILIARY INSTANCE METHODS ====================================
    //== VNOŘENÉ A VNITŘNÍ TŘÍDY ===================================================
    //== TESTOVACÍ TŘÍDY A METODY ==================================================
    }


///#############################################################################
///#############################################################################
///#############################################################################

//== TESTING CLASSES AND METHODS ===============================================
//%A+ >135

    private static void w(){try{
        Thread.sleep(10);}catch(InterruptedException e){}
    }

    /***************************************************************************
     * Testovaci metoda
     */
    public static void test() {
        CM.add(new Rectangle());                    w();
        CM.add(new Ellipse(), new Triangle());   w();
        IO.inform("Výchozí obrázek - budu vyjímat výřez");
        Picture obr = CM.getPicture(50, 0, 75, 75);   w();
        CM.add(obr);                               w();
        CM.setBackgroundColor(NamedColor.BLACK);               w();
        IO.inform("Obrázek přidaný?");
        obr.setPosition(100, 50);                       w();
        IO.inform("Posunutý?");
        obr.setSize(150, 150);                        w();
        IO.inform("Zvětšený?");
        CM.setStepSize(50, 5, 2);
//        CM.setStepSize(1, 50, 50);
//        CM.saveAsPicture(new File("D:/SMAZAT.PNG"));

        System.exit(0);
    }


    /***************************************************************************
     * Testovaci metoda
     */
    public static void test2()
    {
        IO.inform("Hotovo");
        CM.add(new Rectangle   (0, 0, 300, 300));
        CM.add(new Ellipse     (0, 0, 300, 300));
        CM.add(new Triangle(0, 0, 300, 300));
        IO.inform("Nakresleno");

        System.exit(0);
    }
    /** @param args Paremtry příkazového řádku - nepoužité  */
    public static void main(String[] args) { test2(); } /**/
//%A-
    }



///#############################################################################
///#############################################################################
///#############################################################################

/***************************************************************************
 * Rozhraní {@code IPlátno} deklaruje kompletní sadu metod
 * implementovaných třídou {@code Canvas}.
 * Představuje společného rodiče výkonné a testovací třídy,
 * tj. třídy {@code PlátnoBěžné} a třídy {@code PlátnoTestovací}.
 */
interface ISprávce
{
//== KONSTANTY =============================================================
//== DEKLAROVANÉ METODY ====================================================

public void     setStepSize(int krok, int pŠířka, int pVýška);
public int      getStep();
public int      getFieldWidth();
public int      getFieldHeight();
public boolean  isGridVisible();
public void     setGridVisible(boolean zobrazit);
public NamedColor   getBackgroundColor();
public void     setBackgroundColor(NamedColor color);
public String   getName();
public void     setName(String name);
//%A+ >135
public void     setStepSize(int krok, int pŠířka, int pVýška, Object měnič);
public void     setStep(int velikost);
public int      getPixelWidth();
public int      getPixelHeight();
public Size     getSize();
public void     setSize(int columnSize, int rowSize);
public void     setSize(Size rozměr);
public boolean  isVisible();
public void     setVisible(boolean viditelné);
public int      getDrawLevel();
public Position   getPosition();
public void     setPosition(int x, int y);
public void     setPosition(Position pozice);
public Picture  getPicture(int x, int y, int šířka, int výška);
//%A-

public void     redraw();
public void     dontDraw();
public void     returnDraLevel();
public boolean  remove(IPaintable obrazec);
public void     removeAll();
public int      add(IPaintable... obrazec);
public boolean  addOver(IPaintable současný, IPaintable přidaný);
public boolean  addUnder(IPaintable současný, IPaintable přidaný);
//%A+ >135
public void     clockOff();
public void     clockOn();
public boolean  addTop(IPaintable přidaný);
public boolean  addBottom(IPaintable přidaný);
public int      orderOf(IPaintable obrazec);
public List<IPaintable> listOfPainted();
public boolean  addAdaptable(IAdaptable posluchač);
public boolean  removeAdaptable(IAdaptable posluchač);
public boolean  referSizeChanges(boolean hlásit);
public void     addKeyboardListener(KeyListener posluchač);
public void     removeKeyboardListener(KeyListener posluchač);
public void     addMouseListener(MouseListener posluchač);
public void     removeMouseListener(MouseListener posluchač);
public void     saveAsPicture(File soubor);
//%A-

//== ZDĚDĚNÉ METODY ========================================================
//== INTERNÍ DATOVÉ TYPY ===================================================
    }

//%%- ---- Start of the ovejumped text with closing of the envelope ----
////////////////////////////////////////////////////////////////////////////////
//== TESTING CLASSES AND METHODS ===============================================

    /***************************************************************************
     * Test method.
     */
    public static void test()
    {
        IO.inform("Až se pokocháš, stiskni OK");
        System.exit(0);
    }
    /** @param args Parametry příkazového řádku - nepoužívané. */
    public static void main(String[] args)  {  test();  }
}
